﻿#region

using System;
using System.Threading;
using Jiubang.Collections;
using log4net;

#endregion

namespace Jiubang.Communication.Core{
    internal sealed class ObjectPool<T>{
        private static readonly ILog _log = LogManager.GetLogger(typeof (ObjectPool<T>));

        /// <summary>
        /// 对象缓冲为命中时的创建回调方法
        /// </summary>
        private readonly Func<T> _createObject;

        /// <summary>
        /// 对象缓冲池的容量
        /// </summary>
        private readonly int _poolSize;

        private readonly LockFreeStack<T> _stack;

        /// <summary>
        /// 对象创建次数
        /// </summary>
        private int _creationCount;

        /// <summary>
        /// 堆栈中当前存在的对象个数
        /// </summary>
        private int _currentSize;

        /// <summary>
        /// 获取对象时缓存命中次数
        /// </summary>
        private int _hitCount;

        /// <summary>
        /// 对象回收失败次数
        /// </summary>
        private int _recyleFailedCount;

        /// <summary>
        /// 对象回收成功次数
        /// </summary>
        private int _recyleSuccessCount;

        public ObjectPool(int poolSize, Func<T> create){
            if (poolSize <= 0)
                throw new ArgumentOutOfRangeException("poolSize", poolSize, "poolSize必须大于0");
            if (create == null)
                throw new ArgumentNullException("create");

            this._poolSize = poolSize;
            this._createObject = create;
            _stack = new LockFreeStack<T>();
            _creationCount = poolSize/2;
            for (int i = 0; i < _creationCount; i++) Set(create());
        }

        public T Get(){
            T item;
            if (_stack.TryPop(out item)){
                Interlocked.Decrement(ref _currentSize);
                Interlocked.Increment(ref _hitCount);
                if (_log.IsDebugEnabled) _log.DebugFormat("从池中取得一个对象，当前数量还剩余{0}", _currentSize);
                return item;
            }
            if (_log.IsDebugEnabled)
                _log.DebugFormat("池对象使用量上限为{0}，当前数量为{1}，创建临时对象", _poolSize, _currentSize);
            Interlocked.Increment(ref _creationCount);
            return _createObject();
        }

        public bool Set(T args){
            if (Interlocked.Increment(ref _currentSize) < _poolSize){
                Interlocked.Increment(ref _recyleSuccessCount);
                _stack.Push(args);
                if (_log.IsDebugEnabled)
                    _log.DebugFormat("向池中添加一个新对象，池对象上限为{0}，当前数量为{1}", _poolSize, _currentSize);
                return true;
            }
            Interlocked.Increment(ref _recyleFailedCount);
            Interlocked.Decrement(ref _currentSize);
            return false;
        }
    }
}